/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: wait queue
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2023-01-17     JasonHu           Init
 */

#ifndef __WAIT_QUEUE_H___
#define __WAIT_QUEUE_H___

#include <base/spin.h>
#include <base/list.h>
#include <base/thread.h>

#define NX_WAIT_FLAG_ONECE 0x01

typedef struct NX_WaitEntry NX_WaitEntry;

typedef NX_Error (*NX_WaitHandler)(NX_WaitEntry * entry);

struct NX_WaitEntry
{
    NX_List list;
    NX_WaitHandler handler;
    NX_U32 flags;
    void *data;
};

typedef struct NX_WaitQueue
{
    NX_List head;
    NX_Spin lock;
} NX_WaitQueue;

void NX_WaitQueueInit(NX_WaitQueue * queue);
void NX_WaitEntryInit(NX_WaitEntry * entry);
void NX_WaitEntryInitWithHandler(NX_WaitEntry * entry, NX_WaitHandler handler);
void NX_WaitQueuePrepare(NX_WaitQueue * queue, NX_WaitEntry * entry);
void NX_WaitQueueFinish(NX_WaitQueue * queue, NX_WaitEntry * entry);
NX_Error NX_WaitQueueWakeUp(NX_WaitQueue * queue);
NX_Error NX_WaitQueueWakeUpAll(NX_WaitQueue * queue);
NX_Error NX_WaitQueueBlockUnlockTimeout(NX_WaitQueue *queue, NX_Spin *lock, NX_TimeVal tv);

#define NX_WaitQueueBlock(head, condition)              \
    do                                                  \
    {                                                   \
        if (condition)                                  \
            break;                                      \
        NX_WaitEntry entry;                             \
        NX_WaitEntryInit(&entry);                       \
        for (;;)                                        \
        {                                               \
            NX_WaitQueuePrepare(head, &entry);          \
            if (condition)                              \
                break;                                  \
            NX_ThreadBlock(NX_ThreadSelf());            \
        }                                               \
        NX_WaitQueueFinish(head, &entry);               \
    } while (0)
    
#define NX_WaitQueueBlockUnlock(head, lock)              \
    do                                                  \
    {                                                   \
        NX_WaitEntry entry;                             \
        NX_WaitEntryInit(&entry);                       \
        NX_WaitQueuePrepare(head, &entry);              \
        NX_SpinUnlock(lock);                            \
        NX_ThreadBlock(NX_ThreadSelf());                \
        NX_WaitQueueFinish(head, &entry);               \
    } while (0)

#endif /* __WAIT_QUEUE_H___ */
